import * as grpc from "@grpc/grpc-js";
import * as protoLoader from "@grpc/proto-loader";
import { PromiserHandlers } from "./typings";
import { fromPromiserHandlers } from "./util";

export class Server extends grpc.Server {
  constructor(option?: grpc.ChannelOptions) {
    super(option);
  }

  load<ProtoGrpcType, H extends grpc.UntypedServiceImplementation>({
    protoPath,
    service,
    handler,
  }: {
    protoPath: string;
    service: (protoDescriptor: ProtoGrpcType) => grpc.ServiceDefinition;
    handler: PromiserHandlers<H>;
  }) {
    const packageDefinition = protoLoader.loadSync(protoPath, {
      keepCase: true,
      longs: String,
      enums: String,
      defaults: true,
      oneofs: true,
    });
    const protoDescriptor = grpc.loadPackageDefinition(
      packageDefinition
    ) as unknown as ProtoGrpcType;

    this.addService(service(protoDescriptor), fromPromiserHandlers(handler));
    return this;
  }

  on(port: number): Promise<Server> {
    return new Promise((res) => {
      this.bindAsync(
        "0.0.0.0:" + String(port),
        grpc.ServerCredentials.createInsecure(),
        (e) => {
          if (e) throw e;
          this.start();
          res(this);
        }
      );
    });
  }
}
